﻿using GN.Pay.Core.Entities;
using GN.Pay.Core.Repositories;
using GN.Pay.TradeChannel;
using GN.Pay.Core.Services;
using GN.Pay.Utils;
using System;
using static GN.Pay.Core.Consts.ServiceConstant;
using GN.Pay.Core.Exceptions;

namespace GN.Pay.Core.Services.Impl
{
    /// <summary>
    /// 交易退款账本服务
    /// </summary>
    class TradeRefundBookService : APayService, ITradeRefundBookService
    {
        private readonly ITradeRefundBookRepository repostiory;
        private readonly ITradeRefundApplyRepository refundApplyRepository;
        private readonly ITradeAccountBookRepository bookRepostiory;
        private readonly ITradeAccountBookChannelRepository bookChannelRepository;

        /// <summary>
        /// 
        /// </summary>
        /// <param name="repostiory"></param>
        /// <param name="refundApplyRepository"></param>
        /// <param name="bookRepostiory"></param>
        /// <param name="bookChannelRepository"></param>
        public TradeRefundBookService(ITradeRefundBookRepository repostiory, ITradeRefundApplyRepository refundApplyRepository, ITradeAccountBookRepository bookRepostiory,
            ITradeAccountBookChannelRepository bookChannelRepository)
        {
            this.repostiory = repostiory;
            this.refundApplyRepository = refundApplyRepository;
            this.bookRepostiory = bookRepostiory;
            this.bookChannelRepository = bookChannelRepository;
        }

        private TradeRefundBook Accounting(TradeRefundApply apply,
            TradeAccountBookChannel bookChannel, ITradeRefundApplyResult refundApplyResult)
        {
            var book = new TradeRefundBook()
            {
                AppId = apply.AppId,
                AppIp = apply.AppIp,
                ApplyNo = apply.ApplyNo,
                ApplyTime = apply.ApplyTime,
                RefundTradeNo = apply.RefundTradeNo,
                ApplyMoney = apply.ApplyMoney,
                BookId = bookChannel.BookId,
                BookTime = DateTime.Now,
                ChannelId = apply.ChannelId,
                OrderNo = bookChannel.TradeAccountBook.OrderNo,
                Reason = apply.Reason,
                RefundFee = refundApplyResult.RefundFee,
                RefundMoney = refundApplyResult.RefundMoney,
                RefundTime = refundApplyResult.RefundTime.HasValue ? refundApplyResult.RefundTime.Value : DateTime.Now,
                TradeNo = bookChannel.Id
            };
            bookChannel.AddRefundMoney(book);
            bookChannel.TradeAccountBook.AddRefundMoney(book);
            this.repostiory.Insert(book);
            this.refundApplyRepository.Delete(apply.Id);
            this.bookRepostiory.Update(bookChannel.TradeAccountBook);
            return book;
        }

        private void ApplyError(ITradeRefundApplyResult refundApplyResult)
        {
            var oldApply = this.refundApplyRepository.FirstOrDefault(a => a.RefundTradeNo == refundApplyResult.RefundNo);
            if (oldApply == null)
            {
                refundApplyResult.ThrowChannelValidationException("无对应的退款申请执行号:" + refundApplyResult.RefundNo);
            }
            oldApply.ApplyState = refundApplyResult.ResultState == TradeApplyResultState.Apply_Error ? TradeApplyState.Execute_Error : TradeApplyState.Execute_Balance_Error;
            oldApply.ApplyStateMessage = refundApplyResult.ResultStateMessage;
            this.refundApplyRepository.Update(oldApply);
        }

        /// <summary>
        /// 记账
        /// </summary>
        /// <param name="refundApplyResult">退款申请结果</param>
        /// <param name="tradeRefundApply">退款申请</param>
        /// <returns></returns>
        public TradeRefundBook Accounting(ITradeRefundApplyResult refundApplyResult,
            out TradeRefundApply tradeRefundApply)
        {
            if (refundApplyResult == null)
            {
                PayExceptionUtils.ThrowPayValidationException
                    ("refundApplyResult 为 null");
            }
            refundApplyResult.Validation();
            if (refundApplyResult.ResultState == TradeApplyResultState.Apply_Error
                || refundApplyResult.ResultState == TradeApplyResultState.Balance_Insufficient_Error)
            {
                //有错
                this.ApplyError(refundApplyResult);
                tradeRefundApply = null;
                return null;
            }
            if (refundApplyResult.ResultState != TradeApplyResultState.Complete_Success)
            {
                //未完成，需等待
                tradeRefundApply = null;
                return null;
            }
            var book = repostiory.FirstOrDefault(s => s.RefundTradeNo == refundApplyResult.RefundNo);
            if (book != null)
            {
                //已退款
                tradeRefundApply = null;
                return null;
            }
            var apply = this.refundApplyRepository.FirstOrDefault(a => a.RefundTradeNo == refundApplyResult.RefundNo);
            if (apply == null)
            {
                refundApplyResult.ThrowChannelValidationException("无对应的退款申请执行号:" + refundApplyResult.RefundNo);
            }
            var bookChannel = this.bookChannelRepository.Get(apply.TradeNo);
            if (bookChannel == null)
            {
                refundApplyResult.ThrowChannelValidationException("无对应的退款申请交易号:" + apply.TradeNo);
            }
            if (bookChannel.TradeAccountBook == null)
            {
                refundApplyResult.ThrowChannelValidationException("无法获取通道账本Id:" + bookChannel.BookId + " 的父级账本");
            }
            tradeRefundApply = apply;
            return this.Accounting(apply, bookChannel, refundApplyResult);
        }
    }
}
